1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package no.feide.moria.store;
22
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.LinkedList;
26 import java.util.ListIterator;
27
28 import no.feide.moria.log.MessageLogger;
29 import no.feide.moria.store.TicketTTLEvictionPolicy.RegionValue;
30
31 import org.jboss.cache.Fqn;
32 import org.jboss.cache.eviction.EvictedEventNode;
33 import org.jboss.cache.eviction.EvictionAlgorithm;
34 import org.jboss.cache.eviction.EvictionException;
35 import org.jboss.cache.eviction.EvictionPolicy;
36 import org.jboss.cache.eviction.Region;
37
38
39
40
41
42 import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
43 import EDU.oswego.cs.dl.util.concurrent.Sync;
44 import EDU.oswego.cs.dl.util.concurrent.SyncList;
45 import EDU.oswego.cs.dl.util.concurrent.SyncMap;
46 import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
47
48 /***
49 * This eviction algorithm expires cache elements after a fixed period, aka Time To Live.
50 *
51 * @author Bjørn Ola Smievoll <b.o.smievoll@conduct.no>
52 * @version $Revision: 1.10 $
53 */
54 public final class TicketTTLEvictionAlgorithm implements EvictionAlgorithm {
55
56 /*** The logger used by this class. */
57 private final MessageLogger messageLogger = new MessageLogger(TicketTTLEvictionAlgorithm.class);
58
59 /*** Synchronized list of nodes. */
60 private SyncList nodeList;
61
62 /*** Synchronized hash map of nodes. */
63 private SyncMap nodeMap;
64
65 /***
66 * Constructs a new instance.
67 */
68 public TicketTTLEvictionAlgorithm() {
69 super();
70 nodeList = new SyncList(new LinkedList(), new ReentrantWriterPreferenceReadWriteLock());
71 nodeMap = new SyncMap(new HashMap(), new WriterPreferenceReadWriteLock());
72 }
73
74 /***
75 * Perfoms the eviction algorithm. Called periodically.
76 * @see org.jboss.cache.eviction.EvictionAlgorithm#process(org.jboss.cache.eviction.Region)
77 */
78 public void process(final Region region)
79 throws EvictionException {
80
81 while (true) {
82
83 EvictedEventNode node = region.takeLastEventNode();
84
85
86 if (node == null)
87 break;
88
89 Fqn fqn = node.getFqn();
90
91
92
93 if (fqn == null)
94 break;
95
96 Integer event = node.getEvent();
97
98
99 if (event == null) {
100 messageLogger.logWarn("Last event is null for FQN: " + fqn);
101 continue;
102 } else if (event.equals(EvictedEventNode.ADD_EVENT)) {
103 processAddedNodes(region, fqn);
104 messageLogger.logDebug("Got add event");
105 } else if (event.equals(EvictedEventNode.REMOVE_EVENT)) {
106 processRemovedNodes(fqn);
107 messageLogger.logDebug("Got remove event");
108 } else if (event.equals(EvictedEventNode.VISIT_EVENT)) {
109
110 messageLogger.logDebug("Ignoring visit event");
111 } else {
112 messageLogger.logCritical("Illegal event type: " + event);
113 continue;
114 }
115 }
116
117
118 prune(region);
119 }
120
121 public void resetEvictionQueue(final Region region) {
122
123 }
124
125 /***
126 * Adds nodes to eviction data structures.
127 * @param region Region of tree.
128 * @param fqn Fully qualified name.
129 */
130 private void processAddedNodes(final Region region, final Fqn fqn) {
131 TicketTTLEvictionPolicy policy = (TicketTTLEvictionPolicy) region.getEvictionPolicy();
132 RegionValue regionValue = policy.getRegionValue(region.getFqn());
133
134 if (regionValue == null) {
135 messageLogger.logWarn("Values for region not found. Aborting further processing.");
136 return;
137 }
138
139
140 Long nodeEvictionTime = new Long(new Date().getTime() + regionValue.getTimeToLive());
141 NodeEntry node = new NodeEntry(nodeEvictionTime, fqn);
142
143 nodeList.add(node);
144 nodeMap.put(fqn, node);
145 }
146
147 /***
148 * Removes nodes from eviction data structures.
149 * @param fqn Fully qualified name.
150 */
151 private void processRemovedNodes(final Fqn fqn) {
152 NodeEntry node = (NodeEntry) nodeMap.remove(fqn);
153 nodeList.remove(node);
154 }
155
156 /***
157 * Prunes a region of the tree.
158 * @param region Region of tree.
159 * @throws EvictionException
160 * If eviction is interrupted or fails.
161 */
162 private void prune(final Region region)
163 throws EvictionException {
164
165 long now = new Date().getTime();
166
167 EvictionPolicy policy = region.getEvictionPolicy();
168
169 Sync lock = nodeList.writerSync();
170
171 try {
172 lock.acquire();
173 int counter = 0;
174 int nodeListSize = nodeList.size();
175
176 for (ListIterator i = nodeList.listIterator(); i.hasNext();) {
177 NodeEntry node = (NodeEntry) i.next();
178
179 if (now > node.evictionTime.longValue()) {
180 nodeMap.remove(node.fqn);
181 i.remove();
182 counter++;
183
184 try {
185 policy.evict(node.fqn);
186 messageLogger.logDebug("Evicted node " + node.fqn);
187 } catch (Exception e) {
188 messageLogger.logWarn("Eviction failed for FQN: " + node.fqn, e);
189 throw new EvictionException("Eviction failed for fqn: " + node.fqn, e);
190 }
191 } else {
192 break;
193 }
194 }
195
196 messageLogger.logDebug("Number of nodes in region " + region.getFqn() + ": " + nodeListSize
197 + ". Number of evicted nodes: " + counter);
198 } catch (InterruptedException ie) {
199 throw new EvictionException("Node list pruning interrupted", ie);
200 } finally {
201 lock.release();
202 }
203 }
204
205 /***
206 * Represents a cache node.
207 */
208 private static class NodeEntry {
209
210 /*** Eviction time. */
211 private final Long evictionTime;
212
213 /*** Fully qualified name. */
214 private final Fqn fqn;
215
216 /***
217 * Constructs a new instance.
218 *
219 * @param evictionTime Eviction time.
220 * @param fqn Fully qualified name.
221 */
222 public NodeEntry(final Long evictionTime, final Fqn fqn) {
223 this.evictionTime = evictionTime;
224 this.fqn = fqn;
225 }
226 }
227 }